
#pragma once
//#pragma component(browser, off, references)

namespace Configuration
{
	// property base class.
	class CConfBase
	{
		friend class CSectionBase;
		friend class CSectionApp;
	protected:
		int m_nChanges;
		const char* m_szKey;
		const char* m_szComment;
		virtual void save(bool bdiff, FILE* f, const char* szSect = NULL) = 0;
		virtual void Set(const char* szValue) = 0;
		bool Match(const char* szKey)
		{
			return 0 == lstrcmpi(szKey, m_szKey);
		}
	public:
		virtual void Reset() = 0
		{
			m_nChanges++;
		};
		const char* Key() { return m_szKey; };
		CConfBase(const char* szKey, const char* szComment)
			: m_nChanges(0)
			, m_szKey(szKey)
			, m_szComment(szComment)
		{
		};
	};
    class CConfbool : public CConfBase
    {
		bool m_value;
		bool m_default;
    public:
		CConfbool(const char* szKey, bool bDefault, const char* szComment)
			: CConfBase(szKey, szComment)
		{
			m_value = m_default = bDefault;
		};
		~CConfbool(){};
    public:
		bool Value() { return m_value; };
		bool Default() { return m_default; };
		
		operator bool() { return Value(); };
		operator =(bool b) 
		{ 
			if (m_value != b)
				m_nChanges++;
			m_value = b; 
		};
		void Reset() 
		{
			CConfBase::Reset();
			m_value = m_default; 
		};
		virtual void Set(const char* szValue)
		{
			bool b= m_value;
			m_value = false;
			if ((szValue[0] > '0') && (szValue[0] <= '9'))
				m_value = true;
			if (lstrcmpi(szValue, "yes") == 0)
				m_value = true;
			if (lstrcmpi(szValue, "true") == 0)
				m_value = true;
			if (lstrcmpi(szValue, "on") == 0)
				m_value = true;
			if (b != m_value)
				m_nChanges++;
		};
		void save(bool bdiff, FILE* f, const char* szSect = NULL)
		{
			if (bdiff && (0 == m_nChanges)) return;
			if  (!bdiff)
				fprintf(f, "\n# %s, Default value: %d\n", m_szComment, m_default);
			if (szSect && szSect[0])
				fprintf(f, "%s:%s=%d\n", szSect, m_szKey, m_value);
			else
				fprintf(f, "%s=%d\n", m_szKey, m_value);
		};
    };
    class CConfDWORD : public CConfBase
    {
		DWORD m_value;
		DWORD m_default;
    public:
		CConfDWORD(const char* szKey, DWORD bDefault, const char* szComment)
			: CConfBase(szKey, szComment)
		{
			m_value = m_default = bDefault;
		};
		~CConfDWORD(){};
    public:
		DWORD Value() { return m_value; };
		DWORD Default() { return m_default; };
		
		operator DWORD() { return Value(); };
		operator =(DWORD b) 
		{
			if (m_value != b)
				m_nChanges++;
			m_value = b; 
		};
		void Reset() 
		{
			CConfBase::Reset();
			m_value = m_default; 
		};
		virtual void Set(const char* szValue)
		{
			DWORD dw = m_value;
			m_value = strtoul(szValue, NULL, 10);
			if (dw != m_value)
				m_nChanges++;
		};
		void save(bool bdiff, FILE* f, const char* szSect = NULL)
		{
			if (bdiff && (0 == m_nChanges)) return;
			if  (!bdiff)
				fprintf(f, "\n# %s, Default value: %d\n", m_szComment, m_default);
			if (szSect && szSect[0])
				fprintf(f, "%s:%s=%d\n", szSect, m_szKey, m_value);
			else
				fprintf(f, "%s=%d\n", m_szKey, m_value);
		};
    };
    class CConfString : public CConfBase
    {
		char* m_value;
		char* m_default;
    public:
		CConfString(const char* szKey, const char* szDefault, const char* szComment)
			: CConfBase(szKey, szComment)
		{
			int nLen = lstrlen(szDefault) + 2;
			m_value = new char[nLen];
			m_default = new char[nLen];
			lstrcpy(m_value, szDefault);
			lstrcpy(m_default, szDefault);
		};
		~CConfString()
		{
			delete m_value;
			delete m_default;
		};
    public:
		const char* Value() { return m_value; };
		const char* Default() { return m_default; };
		
		operator const char*() { return Value(); };
		operator =(const char* str) 
		{
			if (stricmp(m_value, str) != 0)
				m_nChanges++;
			int nLen = lstrlen(str) + 2;
			delete m_value;
			m_value = new char[nLen];
			lstrcpy(m_value, str);
		};
		void Reset() 
		{
			CConfBase::Reset();

			int nLen = lstrlen(m_default) + 2;
			delete m_value;
			m_value = new char[nLen];
			lstrcpy(m_value, m_default);
		};
		virtual void Set(const char* szValue)
		{
			*this = szValue;
		}
		void save(bool bdiff, FILE* f, const char* szSect = NULL)
		{
			if (bdiff && (0 == m_nChanges)) return;
			if  (!bdiff)
				fprintf(f, "\n# %s, Default value: %s\n", m_szComment, m_default);
			if (szSect && szSect[0])
				fprintf(f, "%s:%s=%s\n", szSect, m_szKey, m_value);
			else
				fprintf(f, "%s=%s\n", m_szKey, m_value);
		};
    };
	
	//Section base class
	class CSectionBase
	{
	protected:
		// keys
		CConfBase** m_keys;
		unsigned int m_nKeyCount;
		//sections
		CSectionBase** m_sects;
		unsigned int m_nSectCount;
		const char* m_name;
	public:
		CSectionBase(const char* szName) : m_name(szName){	};
		//		~CSectionBase(){};
		BOOL PaseLine(char* szKey, const char* szValue)
		{
			char* p = strstr(szKey, ":");
			if (p)
			{
				*p = 0;
				p++;
				CSectionBase* pSub = SectionWith(szKey);
				if (pSub)
					return pSub->PaseLine(p, szValue);
			}
			else
			{
				CConfBase* pk = KeyWith(szKey);
				if (pk)
					return pk->Set(szValue), TRUE;
			}
			return FALSE;
		}
		virtual void save(bool bdiff, FILE* f, const char* szSect = NULL)
		{
			char buff[1024] = {0};
			if (szSect && szSect[0])
				sprintf(buff, "%s:%s", szSect, m_name);
			else
				sprintf(buff, "%s", m_name);
			unsigned int n;
			for (n = 0; n < KeyCount(); n++)
				m_keys[n]->save(bdiff, f, buff);
			for (n = 0; n < SectionCount(); n++)
				m_sects[n]->save(bdiff, f, buff);
		};
		virtual void Reset()
		{
			for(unsigned int n = 0; n < KeyCount(); n++)
				KeyAt(n)->Reset();
		};
		unsigned int KeyCount() { return m_nKeyCount; };
		unsigned int SectionCount() { return m_nSectCount; };
		CConfBase* KeyAt(unsigned int nIndex)
		{
			if (nIndex >= m_nKeyCount)
				return NULL;
			return m_keys[nIndex];
		};
		CConfBase* KeyWith(const char* szKey)
		{
			for(unsigned int n = 0; n < KeyCount(); n++)
			{
				if (m_keys[n]->Match(szKey))
					return m_keys[n];
			}
			return NULL;
		};
		CSectionBase* SectionAt(unsigned int nIndex)
		{
			if (nIndex >= m_nSectCount)
				return NULL;
			return m_sects[nIndex];
		};
		CSectionBase* SectionWith(const char* szSect)
		{
			for(unsigned int n = 0; n < SectionCount(); n++)
				if (0 == lstrcmpi(m_sects[n]->m_name, szSect))
					return m_sects[n];
				return NULL;
		};
	};
	//Global Section 
	class CSectionApp : public CSectionBase
	{
	public:
		CSectionApp(const char* szName) : CSectionBase(szName)
		{
		};
		//		~CSectionApp(){};
	protected:
		virtual void save(bool bdiff, FILE* f, const char* szSect = NULL)
		{
			unsigned int n;
			for (n = 0; n < KeyCount(); n++)
				m_keys[n]->save(bdiff, f);
			for (n = 0; n < SectionCount(); n++)
				m_sects[n]->save(bdiff, f);
		};
	public:
		BOOL LoadGlobal()
		{
			char buff[1024] = {0};
			DWORD nLen = GetModuleFileName(0, buff, 1024);
			buff[nLen - 3] = 0;
			lstrcat(buff, "conf");
			if (0xFFFFFFFF == GetFileAttributes(buff))
			{
				WriteGlobalProfile();
				return TRUE;
			}
			else
				return LoadUserProfile(buff);
		};
		BOOL LoadUserProfile(const char* szFile)
		{
			FILE* f = fopen(szFile, "r");
			if (!f) return FALSE;
			char buff[1024] = {0};
			while(fgets(buff, 1024, f))
			{
				if (!IsCharAlpha(buff[0]))	// comment
					continue;
				char* pv = strstr(buff, "=");
				if (!pv || !*pv)	// invalid line.
					continue;
				*pv = 0; pv++;
				char* p = strstr(buff, ":");
				if (p)
				{
					*p = 0;
					p++;
					CSectionBase* pSub = SectionWith(buff);
					if (pSub)
						pSub->PaseLine(p, pv);
				}
				else
				{
					CConfBase* pk = KeyWith(buff);
					if (pk)
						pk->Set(pv);
				}
			}
			fclose(f);
			return TRUE;
		};
		
		void WriteUserProfile(const char* szFile)
		{
			FILE* f = fopen(szFile, "w+");
			if (!f) return;
			save(true, f);
			fclose(f);
		};
		
		void WriteGlobalProfile()
		{
			char buff[1024] = {0};
			DWORD n = GetModuleFileName(0, buff, 1024);
			buff[n - 3] = 0;
			lstrcat(buff, "conf");
			
			FILE* f = fopen(buff, "w+");
			if (!f) return;
			save(false, f);
			fclose(f);
		};
	};
};